package org.jeecg.modules.flowable.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.jeecg.modules.flowable.common.util.IdUtil;
import org.jeecg.modules.flowable.common.util.json.ObjectConvert;
import org.jeecg.modules.flowable.common.util.validation.AssertValid;
import org.jeecg.modules.flowable.common.util.validation.MustParamValidation;
import org.jeecg.modules.flowable.controller.dto.FlowStartRequest;
import org.jeecg.modules.flowable.dic.MatterFlowStatus;
import org.jeecg.modules.flowable.entity.*;
import org.jeecg.modules.flowable.exception.ModuleFlowableException;
import org.jeecg.modules.flowable.mapper.*;
import org.jeecg.modules.flowable.service.*;
import org.jeecg.modules.flowable.service.bo.Attr;
import org.jeecg.modules.flowable.service.bo.FengForm;
import org.jeecg.modules.flowable.service.bo.Matter;
import org.jeecg.modules.flowable.service.bo.TaskConfList;
import org.jeecg.modules.flowable.service.bo.flow.FlowInst;
import org.jeecg.modules.flowable.service.oform.OformStart;
import org.jeecg.modules.flowable.system.core.SystemUser;
import org.jeecg.modules.flowable.system.etc.SystemUserUtil;
import org.jeecg.modules.flowable.util.ObjectUtil;
import org.jeecg.modules.flowable.util.SpringContextUtil;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * Title:  <br>
 * Desc:  <br>
 * Date: 6/16/22 12:28 PM <br>
 * Company: 联通（辽宁）产业互联网有限公司 <br>
 *
 * @author zxd
 * @version 1.0.0
 */
@Service
@Slf4j
public class MatterServiceImpl implements MatterService {

    @Autowired
    private ExtFlowableMapper extFlowableMapper;
    @Autowired
    private TActMatterMapper tActMatterMapper;
    @Autowired
    private ITActMatterService iTActMatterService;
    @Autowired
    private FormService formService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TActMatterInstMapper tActMatterInstMapper;
    @Autowired
    private TActMatterInstFormdataMapper tActMatterInstFormdataMapper;
    @Autowired
    private ITActMatterInstFormdataService iTActMatterInstFormdataService;
    @Autowired
    private TActRuActinstAttrMapper tActRuActinstAttrMapper;
    @Autowired
    private ITActRuActinstAttrService iTActRuActinstAttrService;
    @Autowired
    private TActRuActinstMapper tActRuActinstMapper;
    @Autowired
    private TActFengFormDataMapper tActFengFormDataMapper;
    @Autowired
    private TActRuTaskScopeMapper tActRuTaskScopeMapper;
    @Autowired
    private ITActRuTaskScopeService iTActRuTaskScopeService;

    @Autowired
    private FlowableService flowableService;

    @Override
    public Page<Matter> queryMatterList(String matterName, String matterType, Integer argCurrPage, Integer argPageSize) {

        Page<Matter> page = extFlowableMapper.queryMatterList(new Page(argCurrPage, argPageSize), matterName, matterType);
        return page;
    }

    @Override
    public Matter save(TActMatterEntity argTActMatterEntity) {
        MustParamValidation.mustValidation(argTActMatterEntity.getMatterName(), argTActMatterEntity.getMatterType());
        SystemUser loginUser = SystemUserUtil.getSystemUser();
        DateTime now = DateTime.now();
        if (AssertValid.isEmpty(argTActMatterEntity.getId())) {
            // 新增
            // 验证表单KEY是否存在
            if (AssertValid.isNotEmpty(argTActMatterEntity.getFormKey())) {
                FengForm fengForm = formService.queryFormByKey(argTActMatterEntity.getFormKey());
                if (AssertValid.isEmpty(fengForm))
                    throw new ModuleFlowableException("表单KEY不存在");
            }
            // 验证流程KEY是否存在
            if (AssertValid.isNotEmpty(argTActMatterEntity.getFlowKey())) {
                List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey(argTActMatterEntity.getFlowKey()).latestVersion().active().list();
                if (AssertValid.isEmpty(list)) {
                    throw new ModuleFlowableException("流程KEY不存在");
                }
            }
            argTActMatterEntity.setId(IdUtil.getStringId()).setCreateBy(loginUser.getRealname()).setCreateTime(now.toDate()).setUpdateBy(loginUser.getRealname()).setUpdateTime(now.toDate()).setDelFlag(0);
            argTActMatterEntity.setMatterStatus(1);
            tActMatterMapper.insert(argTActMatterEntity);

        } else {
            // 修改
            if (AssertValid.isNotEmpty(argTActMatterEntity.getFormKey())) {
                FengForm fengForm = formService.queryFormByKey(argTActMatterEntity.getFormKey());
                if (AssertValid.isEmpty(fengForm))
                    throw new ModuleFlowableException("表单KEY不存在");
            }
            // 验证流程KEY是否存在
            if (AssertValid.isNotEmpty(argTActMatterEntity.getFlowKey())) {
                List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey(argTActMatterEntity.getFlowKey()).latestVersion().active().list();
                if (AssertValid.isEmpty(list)) {
                    throw new ModuleFlowableException("流程KEY不存在");
                }
            }
            argTActMatterEntity.setUpdateBy(loginUser.getRealname()).setUpdateTime(now.toDate()).setDelFlag(0);
            LambdaQueryWrapper<TActMatterEntity> query = new LambdaQueryWrapper<>();
            query.eq(TActMatterEntity::getId, argTActMatterEntity.getId());
            tActMatterMapper.update(argTActMatterEntity, query);
        }
        List<Matter> matters = extFlowableMapper.queryMatterById(argTActMatterEntity.getId());
        return matters.get(0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FlowInst flowStart(FlowStartRequest argFlowStartRequest) {
        String argMatterId = argFlowStartRequest.getMatterId();
        String argTitle = argFlowStartRequest.getTitle();
        String formKey = argFlowStartRequest.getFormKey();
        String formContent = argFlowStartRequest.getFormContent();

        MustParamValidation.mustValidation(argMatterId, argTitle, formKey, formContent);

        String argMaterInstId = argFlowStartRequest.getMatterInstId();
        Map<String, Object> argFormData = argFlowStartRequest.getFormData();
        List<Attr> argAttrList = argFlowStartRequest.getAttrList();

        SystemUser loginUser = SystemUserUtil.getSystemUser();
        DateTime now = DateTime.now();
        List<Matter> matters = extFlowableMapper.queryMatterById(argMatterId);
        if (AssertValid.isEmpty(matters) || matters.get(0).getMatterStatus().intValue() != 1) {
            log.error("事项不存在");
            throw new ModuleFlowableException("事项不存在");
        }


        Date firstTime = new Date();
        Boolean isUndo = false;
        String hisIds = "";
        // 验证原事项状态，如果是已退回的事项可以重新提交
        if (AssertValid.isNotEmpty(argMaterInstId)) {
            LambdaQueryWrapper<TActMatterInstEntity> qq = new LambdaQueryWrapper<>();
            qq.eq(TActMatterInstEntity::getId, argMaterInstId);
            qq.eq(TActMatterInstEntity::getDelFlag, 0);
            List<TActMatterInstEntity> tActMatterInstEntities = tActMatterInstMapper.selectList(qq);
            if (AssertValid.isEmpty(tActMatterInstEntities)) {
                throw new ModuleFlowableException("原事项实例不存在");
            }
            Integer status = tActMatterInstEntities.get(0).getStatus();
            firstTime = tActMatterInstEntities.get(0).getFirstTime();
            hisIds = tActMatterInstEntities.get(0).getHisIds();
            if (AssertValid.isNotEmpty(status) && (status.intValue() == MatterFlowStatus.UNDO.intValue() || status.intValue() == MatterFlowStatus.START.intValue())) {
                if (status.intValue() == MatterFlowStatus.UNDO.intValue()) {
                    log.info("发起流程-回退至发起者的情况更新原流程状态");
                    // 如果是被退回的，修改原事项实例状态为退回已提交。
                    TActMatterInstEntity ie = new TActMatterInstEntity();
                    ie.setRestartTime(now.toDate());
                    ie.setStatus(MatterFlowStatus.UNDO_SUBMIT);
                    ie.setUpdateBy(loginUser.getRealname());
                    ie.setUpdateTime(now.toDate());
                    tActMatterInstMapper.update(ie, qq);
                }
                if (status.intValue() == MatterFlowStatus.START.intValue()) {
                    log.info("发起流程-草稿状态或被撤销的流程，删除原流程运转过程中产生的各种数据");
                    isUndo = true;
                    // 如果是被撤消或草稿状态的，删掉原任务实例数据
                    // 因为如果是撤消的情况，以行业经验来看系统是不保留任何痕迹的，所以是物理删除，不能用逻辑删除，致远就是这样做的
                    tActMatterInstMapper.deleteById(argMaterInstId);
                    // 删除过程数据
                    LambdaQueryWrapper<TActRuActinstEntity> lq = new LambdaQueryWrapper<>();
                    lq.eq(TActRuActinstEntity::getActMatterInstId, argMaterInstId);
                    List<TActRuActinstEntity> tActRuActinstEntities = tActRuActinstMapper.selectList(lq);
                    tActRuActinstMapper.delete(lq);
                    // 删除附件数据
                    LambdaQueryWrapper<TActRuActinstAttrEntity> lq2 = new LambdaQueryWrapper<>();
                    lq2.eq(TActRuActinstAttrEntity::getProcInstId, tActRuActinstEntities.get(0).getProcInstId());
                    tActRuActinstAttrMapper.delete(lq2);
                    // 删除过程表单数据T_ACT_FENG_FORM_DATA
                    LambdaQueryWrapper<TActFengFormDataEntity> lq3 = new LambdaQueryWrapper<>();
                    lq3.eq(TActFengFormDataEntity::getProcInstId, tActRuActinstEntities.get(0).getProcInstId());
                    tActFengFormDataMapper.delete(lq3);
                    // 删除流程表单数据T_ACT_MATTER_INST_FORMDATA
                    LambdaQueryWrapper<TActMatterInstFormdataEntity> lq4 = new LambdaQueryWrapper<>();
                    lq4.eq(TActMatterInstFormdataEntity::getActMatterInstId, argMaterInstId);
                    tActMatterInstFormdataMapper.delete(lq4);
                }
            } else {
                log.error("只有被退回或草稿状态的的事项才允许提交");
                throw new ModuleFlowableException("只有被退回或草稿状态的的事项才允许提交");
            }

        }
        Matter matter = matters.get(0);
        // 查询事项绑定流程的情况
        if (AssertValid.isEmpty(matter.getFlowKey())) {
            log.error("未绑定流程");
            throw new ModuleFlowableException("未绑定流程");
        } else {
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey(matter.getFlowKey()).latestVersion().active().list();
            if (AssertValid.isEmpty(list)) {
                log.error("流程不存在或状态异常");
                throw new ModuleFlowableException("流程不存在或状态异常");
            }
        }
        // 查询表单绑定的情况
        FengForm fengForm = null;
        if (AssertValid.isNotEmpty(matter.getFormKey())) {
            fengForm = formService.queryActiveFormByKey(matter.getFormKey());
            if (AssertValid.isEmpty(fengForm)) {
                log.error("绑定的表单不存在或状态异常");
                throw new ModuleFlowableException("绑定的表单不存在或状态异常");
            }
        }
        TActMatterInstEntity tActMatterInstEntity = new TActMatterInstEntity().setId(IdUtil.getStringId()).setCreateBy(loginUser.getRealname()).setCreateTime(now.toDate()).setUpdateBy(loginUser.getRealname()).setUpdateTime(now.toDate()).setDelFlag(0);
        tActMatterInstEntity.setMatterTitle(argTitle);
        if (AssertValid.isNotEmpty(fengForm))
            tActMatterInstEntity.setActFengFormId(fengForm.getId());
        tActMatterInstEntity.setActMatterId(argMatterId);
        tActMatterInstEntity.setStartTime(now.toDate());
        tActMatterInstEntity.setStartUserId(loginUser.getId());
        tActMatterInstEntity.setStartUserName(loginUser.getRealname());
        tActMatterInstEntity.setStatus(MatterFlowStatus.PROCING);
        tActMatterInstEntity.setFirstTime(tActMatterInstEntity.getFirstTime());

        tActMatterInstEntity.setFormKey(formKey);
        tActMatterInstEntity.setFormContent(formContent);

        if (!isUndo) {
            // 因为撤消的状态数据库中不保留任何痕迹，所以不需要记录原单号，因为原事项实例单已经被物理删除了。isUndo为false的时候为正常的流程的是交，如果有原单则记录下原单号。
            tActMatterInstEntity.setOriMatterInstId(argMaterInstId);
            tActMatterInstEntity.setHisIds(argMaterInstId);
            if (hisIds == null)
                hisIds = "";
            else
                hisIds = hisIds.trim();
            if (hisIds.trim().equals("")) {
                tActMatterInstEntity.setHisIds(argMaterInstId);
            } else {
                tActMatterInstEntity.setHisIds(hisIds + "," + argMaterInstId);
            }

            tActMatterInstEntity.setFirstTime(firstTime);
        }

        // 创建流程变量
        Map<String, Object> var = new HashMap<>();
        if (AssertValid.isNotEmpty(argFormData)) {
            Iterator<String> iterator = argFormData.keySet().iterator();
            while (iterator.hasNext()) {
                String item = iterator.next();

                Object o = argFormData.get(item);
                String formDataType = ObjectUtil.getFormDataType(o);
                if (formDataType.equals("list") || formDataType.equals("object")) {
                    Object o1 = argFormData.get(item);
                    String s = JSONObject.toJSONString(o1);
                }
                var.put(item, argFormData.get(item));
            }
        }
        // 保存附件数据
        int i = 0;
        List<TActRuActinstAttrEntity> objects2 = new ArrayList<>();
        ;
        if (AssertValid.isNotEmpty(argAttrList)) {

            for (Attr attr : argAttrList) {
                TActRuActinstAttrEntity tActRuActinstAttrEntity = new TActRuActinstAttrEntity().setId(IdUtil.getStringId()).setCreateBy(loginUser.getRealname()).setCreateTime(now.toDate()).setUpdateBy(loginUser.getRealname()).setUpdateTime(now.toDate()).setDelFlag(0);
                tActRuActinstAttrEntity.setActRuActinstId(tActMatterInstEntity.getId());
                String extName = "";
                if (attr.getOriFileName() != null) {
                    String[] split = attr.getOriFileName().split("\\.");
                    if (split.length > 1)
                        extName = attr.getOriFileName().split("\\.")[1];
                }
                tActRuActinstAttrEntity.setFileExtension(extName.toLowerCase());
                tActRuActinstAttrEntity.setFsUri(attr.getUri());
                tActRuActinstAttrEntity.setInx(++i);
                tActRuActinstAttrEntity.setIsStart(1);
                tActRuActinstAttrEntity.setOriFileName(attr.getOriFileName());
                tActRuActinstAttrEntity.setProcInstId(tActMatterInstEntity.getProcInstId());
                tActRuActinstAttrEntity.setSysFlId(attr.getSysFlId());
                tActRuActinstAttrEntity.setSysFsId(attr.getSysFsId());
                objects2.add(tActRuActinstAttrEntity);
            }
        }

        Map<String, Object> formContentMap = new HashMap<>();
        if (AssertValid.isNotEmpty(formContent)) {
            formContentMap = JSONObject.parseObject(formContent).getInnerMap();
            log.info("Biz form data: {}", formContentMap);
        }
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(matter.getFlowKey(), tActMatterInstEntity.getId(), formContentMap);
        // 启动流程时的流程变量，这个流程变量需要从自定义表单中获取表单数据，当做流程的变量启动。
        // ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(matter.getFlowKey(), tActMatterInstEntity.getId(), var);
        // 工作流引擎的流程实例ID回写到事项实例表中，用来做业务关联
        tActMatterInstEntity.setProcInstId(processInstance.getProcessInstanceId());
        tActMatterInstMapper.insert(tActMatterInstEntity);

        // 创建流程表单数据到引擎中
        // 第一步：业务表单数据
        String oFormBeanName = "OformStart_" + matters.get(0).getFlowKey();
        Object bean = null;
        try {
            bean = SpringContextUtil.getBean(oFormBeanName);
        } catch (Exception e) {
            // 未配置业务逻辑的处理类时会抛异常，此时不需要做任何处理。
        }
        if (bean != null && bean instanceof OformStart) {
            ((OformStart) bean).deal(processInstance.getProcessInstanceId(), tActMatterInstEntity.getId(), argMatterId, argTitle, argFormData, argAttrList);
        }
        // 第二步：引擎表单
        List<TActMatterInstFormdataEntity> objects = new ArrayList<>();
        ;
        if (AssertValid.isNotEmpty(argFormData)) {
            Iterator<String> iterator = argFormData.keySet().iterator();
            while (iterator.hasNext()) {
                String item = iterator.next();
                TActMatterInstFormdataEntity tActMatterInstFormdataEntity = new TActMatterInstFormdataEntity().setId(IdUtil.getStringId()).setCreateBy(loginUser.getRealname()).setCreateTime(now.toDate()).setUpdateBy(loginUser.getRealname()).setUpdateTime(now.toDate()).setDelFlag(0);
                if (fengForm != null)
                    tActMatterInstFormdataEntity.setActFengFormId(fengForm.getId());
                tActMatterInstFormdataEntity.setActMatterInstId(tActMatterInstEntity.getId());
                tActMatterInstFormdataEntity.setProcInstId(tActMatterInstEntity.getProcInstId());
                tActMatterInstFormdataEntity.setFormItem(item);

                Object o = argFormData.get(item);
                String formDataType = ObjectUtil.getFormDataType(o);
                tActMatterInstFormdataEntity.setDataType(formDataType);
                if (formDataType.equals("list") || formDataType.equals("object")) {
                    Object o1 = argFormData.get(item);
                    String s = JSONObject.toJSONString(o1);
                    tActMatterInstFormdataEntity.setFormValue(s);
                } else {
                    if (o != null)
                        tActMatterInstFormdataEntity.setFormValue(o.toString());
                }
                objects.add(tActMatterInstFormdataEntity);
            }
        }

        objects.forEach(s2 -> {
            s2.setProcInstId(processInstance.getProcessInstanceId());
        });
        // 保存表单
        iTActMatterInstFormdataService.saveBatch(objects);
        objects2.forEach(s2 -> {
            s2.setProcInstId(processInstance.getProcessInstanceId());
        });
        // 保存附件
        iTActRuActinstAttrService.saveBatch(objects2);
        log.info("[流程引擎]-{}事项流程发起成功，事项ID:{},事项名称:{},流程KEY:{},流程实例ID:{}。", argTitle, argMatterId, matters.get(0).getMatterName(), matters.get(0).getFlowKey(), processInstance.getProcessInstanceId());
        // 创建流程运行时任务范围数据
        List<TaskConfList> taskConfList = flowableService.getTaskConfList(processInstance.getProcessDefinitionId());
        List<TActRuTaskScopeEntity> enList = new ArrayList<>();
        for (TaskConfList confList : taskConfList) {
            TActRuTaskScopeEntity en = new TActRuTaskScopeEntity().setId(IdUtil.getStringId()).setCreateBy(loginUser.getRealname()).setCreateTime(now.toDate()).setUpdateBy(loginUser.getRealname()).setUpdateTime(now.toDate()).setDelFlag(0);
            en.setId(IdUtil.getStringId());
            en.setActMatterInstId(tActMatterInstEntity.getId());
            if (confList.getRelative() != null)
                en.setIsRelative(confList.getRelative() == true ? 1 : 0);
            else
                en.setIsRelative(0);
            en.setTaskDefKey(confList.getTaskId());

            if (confList.getUserIds() != null && !confList.getUserIds().trim().equals("")) {
                // 首先，如果用户ID配置不为空的情况，则增加一个运行时的用户范围数据，它的优先级最高。
                String[] split = confList.getUserIds().split(",");
                TActRuTaskScopeEntity en2 = ObjectConvert.convert(en, TActRuTaskScopeEntity.class);
                for (String s : split) {
                    en2.setOptId(s);
                    en2.setTaskScopeType(2);
                    enList.add(en2);
                }
            } else if (confList.getRoleIds() != null && !confList.getRoleIds().trim().equals("")) {
                // 其次，如果角色ID配置不为空的情况，则增加一个角色的用户范围数据，它的优先级其次。
                String[] split = confList.getRoleIds().split(",");
                TActRuTaskScopeEntity en2 = ObjectConvert.convert(en, TActRuTaskScopeEntity.class);
                for (String s : split) {
                    en2.setOptId(s);
                    en2.setTaskScopeType(1);
                    enList.add(en2);
                }
            } else {
                // 最后，如果即没有用户配置又没有角色配置，则该任务自己创建自己审批。
                TActRuTaskScopeEntity en2 = ObjectConvert.convert(en, TActRuTaskScopeEntity.class);
                en2.setOptId(loginUser.getId());
                en2.setTaskScopeType(2);
                enList.add(en2);
            }
        }

        // 批量保存运行时的用户范围数据
        iTActRuTaskScopeService.saveBatch(enList);
        FlowInst flowInst = new FlowInst().setMatterInst(tActMatterInstEntity.getId()).setMatterId(argMatterId).setProcessInstanceId(processInstance.getProcessInstanceId());
        return flowInst;
    }

    @Override
    public void delete(String argId) {
        MustParamValidation.mustValidation(argId);
        SystemUser loginUser = SystemUserUtil.getSystemUser();
        DateTime now = DateTime.now();
        TActMatterEntity tActMatterInstEntity = new TActMatterEntity().setUpdateBy(loginUser.getRealname()).setUpdateTime(now.toDate()).setDelFlag(1);
        LambdaQueryWrapper<TActMatterEntity> query = new LambdaQueryWrapper<TActMatterEntity>();
        query.eq(TActMatterEntity::getId, argId);
        tActMatterMapper.update(tActMatterInstEntity, query);
    }
}